//
// Macroscopic model of the Internet, for modeling worm epidemics etc.
//
// @author Michael Liljenstam, ISTS, Dartmouth College
//
// $Id: MacroscopicModel.java,v 1.10 2003/03/08 02:02:36 mili Exp $

package SSF.App.Worm;

//import java.io.*;
import java.util.*;

import com.renesys.raceway.SSF.*;
import cern.jet.random.*;

/*
import EDU.auburn.VGJ.*;
import EDU.auburn.VGJ.graph.*;
import EDU.auburn.VGJ.util.*;
import EDU.auburn.VGJ.algorithm.*;
import EDU.auburn.VGJ.algorithm.shawn.*;
import java.awt.Frame;
*/


/** 
 *  Macroscopic model of the Internet, for modeling worm epidemics etc.
 *  Models the global infection spreading through 
 *  "the whole Internet" at a macroscopic level, as opposed to selective 
 *  parts where hosts and routers are modeled explicitly.
 *
 *  <p>
 *  Assumes that the worm is spreading through random scanning, and models 
 *  worm propagation through a deterministic discrete time epidemiological 
 *  model. The MacroscopicModel class (singleton class) creates an internal 
 *  macroscopic representation of the Internet at the interdomain level 
 *  (AS level). The simplest representation is the "homogeneous model", 
 *  where AS boundaries are ignored and the Internet as treated as one 
 *  big network with homogeneous interactions. For someone interested in 
 *  studying interactions between ASes it's necessary to "partition" the 
 *  model up in ASes with a representation of the AS-level topology, in 
 *  epidemic modeling terms this is know as a "stratified model".
 *  The ASGraph class handles loading an AS adjacency file and building the 
 *  internal topology representation.
 *  </p><p>
 *  Each <tt>AS</tt> contains an object of class <tt>WormEpidemicState</tt> 
 *  which handles the epidemic state of the AS. It also contains an object of 
 *  class <tt>WormTrafficState</tt> that handles the state of worm scans 
 *  generated by the worm.
 *  </p>
 *  <p>
 *  TO DO:s <br>
 *  <ul>
 *    <li> "Everything is public" code clean up.</li>
 *    <li> Move global config from proto session to this class.</li>
 *  </ul>
 *  </p>
 */
public class MacroscopicModel extends Entity {

  // model globals

  /** Global ref to this singleton object. */
  public static MacroscopicModel ref;

  /** Time. */
  float t = 0; // in seconds

  /** Internet */
  public ASGraph net;

  // submodels
  /** Worm epidemic model (global). */
  public WormEpidemic  wormModel;
  /** Worm-induced traffic model (global). */
  public WormTraffic   wormTraffic;

  /** Time step. */
  float DeltaT; // set when updating

  /** Infection RNG. */
  public RandomStream infRNG;
  /** Seed for infection RNG. */
  public String       infSeed = "infSeed1";
  /** Use Binomial distr for picking number of infections. */
  Binomial             infBinomialDistr;
  /** Use a U[0,1] for picking network host. */
  Uniform              infUniformDistr;

  // etc
  public float layoutRectSize = 1000F;

  public boolean DEBUG = false;

  // keep track of when network model level objects have been registered
  public boolean ssfHostsRegistered = false;
  public boolean ssfRoutersRegistered = false;

  /**
   * @param topologyFile  Adjacency list formatted file of interdomain 
   *                      topology. May be <tt>null</tt> for homogeneous model.
   * @param rngSeed       Seed string for infection RNG.
   * @param debugOn       Set to <tt>true</tt> for debug output.
   * @param epidemicClass Name of class to implement the epidemic model.
   * @param initializerClass Name of class to provide initialization code for 
   *                         epidemic model.
   * @param trafficClass  Name of class to implement the model of how much 
   *                      scan traffic is generated by the worm. May be 
   *                      <tt>null</tt>, meaning that traffic is not modeled.
   * @param s_0           Initial number of susceptible (vulnerable) hosts.
   * @param i_0           Initial number of infected hosts.
   * @param _beta         Overall infection parameter.
   * @param meanScanRate  Mean rate of scans [scans/s] generated by a single 
   *                      worm. (Used only if traffic model is used.)
   */
  public MacroscopicModel(String topologyFile, String rngSeed, boolean debugOn,
			  String epidemicClass, String initializerClass, 
			  String gammaClass, String trafficClass, 
			  int s_0, int i_0, float _beta,
			  float meanScanRate) {
    ref = this;

    DEBUG = debugOn;
    if (DEBUG) {
	System.out.println("Creating epidemic model from topology file " 
			   + topologyFile);
    }

    // init RNG
    //    infRNG = Net.accessRandomStream(this, "MacroscopicModel");
    if (rngSeed != null) {
	infSeed = rngSeed;
    }
    try {
      infRNG = RandomStream.getStream(this,
				      "MersenneTwister",
				      infSeed);

    } catch (RandomStream.StreamException rngex) {
      rngex.printStackTrace();
      System.exit(-1);
    }
    infUniformDistr = new Uniform(0, 1, infRNG);

    // init network
    net = new ASGraph(topologyFile);
    net.DEBUG = DEBUG;

    // create submodels ......................
    try { // epidemic model, global object
	Class eclass = Class.forName(epidemicClass);
	Object eobj = eclass.newInstance();
	wormModel = (WormEpidemic) eobj;
    } catch (Exception any) {
	System.err.println("Can't use WormEpidemic "+epidemicClass+":");
	any.printStackTrace();
    }
    if (epidemicClass.endsWith("DeterministicWormEpidemic")) {  // removals 
	if (gammaClass != null) {
	    ((DeterministicWormEpidemic)wormModel).setGammaFunction(gammaClass);
	} else {
	    System.err.println("ERROR: gamma class cannot be null for DeterministicWormEpidemic");
	    System.exit(-1);
	}
    }
    if (epidemicClass.endsWith("StochasticWormEpidemic")) {  // removals 
	if (gammaClass != null) {
	    ((StochasticWormEpidemic)wormModel).setGammaFunction(gammaClass);
	} else {
	    System.err.println("ERROR: gamma class cannot be null for StochasticWormEpidemic");
	    System.exit(-1);
	}
    }
    if (trafficClass != null) { // use traffic model?
	try { // traffic model, global object
	    Class tclass = Class.forName(trafficClass);
	    Object tobj = tclass.newInstance();
	    wormTraffic = (WormTraffic) tobj;
	} catch (Exception any) {
	    System.err.println("Can't use WormTraffic "+trafficClass+":");
	    any.printStackTrace();
	}
    }
    // ........................................

    // startup values for epidemic model
    wormModel.sTot = s_0; 
    wormModel.iTot = i_0;
    wormModel.Beta = _beta;
    wormModel.N = s_0 + i_0 + Math.round(wormModel.rTot); // total number of hosts in system

    // initialize submodels
    try { // create initializer
	Class iclass = Class.forName(initializerClass);
	Object iobj = iclass.newInstance();
	wormModel.initializer = (WormEpidemicInitializer) iobj;
    } catch (Exception any) {
	System.err.println("Can't use WormEpidemicInitializer "+
			   initializerClass+":");
	any.printStackTrace();
    }
    wormModel.init();
    if (wormTraffic != null) {
	if (trafficClass.endsWith("MeanRateWormTraffic")) {
	    ((MeanRateWormTraffic)wormTraffic).setMeanScanRate(meanScanRate);
	}
    }
  }


  //
  // Methods for programmatic use
  //
    
  /**
   *  Register a host, actually its WormProtocolSession, with the global 
   *  epidemic model.
   *  @param _host  The calling host.
   *  @param asn    AS number of the AS the host belongs to.
   */
  public void registerHost(WormProtocolSession _host, int asn) {
    if (DEBUG) {
      String hostNH = ((SSF.Net.Host) _host.inGraph()).nhi;
      System.err.println("Registering host " + hostNH + " in ASN " + asn);
    }
    int index;
    if (net.numASs == 1) { 
	// homogeneous model
	index = asn-1; // if non-stratified model
	net.ases[index].asNumber = asn;
    } else {
	// stratified model - lookup index from lookup table
	index = net.asnToIndex(asn);

	// asNumber attr already set
    }
    net.ases[index].susceptibleHostList.add(_host);
    ssfHostsRegistered = true;
  }

  /**
   *  Register a border router, actually its RouterProtocolSession, with the 
   *  global epidemic model.
   *  @param _router  The calling router.
   *  @param asn      AS number of the AS the router belongs to.
   */
  public void registerBorderRouter(GatewayProtocolSession _router, int asn) {
    if (DEBUG) {
      String routerNH = ((SSF.Net.Host) _router.inGraph()).nhi;
      System.err.println("Registering border router " + routerNH + " in ASN " + asn);
    }
    int index;
    if (net.numASs == 1) { 
	// homogeneous model
	index = asn-1; // if non-stratified model
	net.ases[index].asNumber = asn;
    } else {
	// stratified model - lookup index from lookup table
	index = net.asnToIndex(asn);

	// asNumber attr already set
    }
    net.ases[index].borderRouterList.add(_router);
    ssfRoutersRegistered = true;
  }

  /**
   *  Update the timestepped model.
   *  @param newTime  Step forward one step to this time (in hours).
   *  @return New simulation time.
   */
  public float update(float newTime) {
    DeltaT = newTime - t;
    // update
    wormModel.update();
    if (wormTraffic != null) {
	wormTraffic.update();
    }
    t = newTime;
    return t;
  }

  /**
   *  Get the total number of infectioned hosts in the whole system.
   */
  public float getTotalInfections() {
    float iTot = 0;
    for (int j = 0; j < net.numASs; j++) {
      iTot += net.ases[j].wormModel.i;
    }
    return iTot;
  }
}
